/*
 * MPEGFrame.cpp
 *      -- define frame format for MPEG file
 *
 * Author: Qiu Shuang
 * Date: Mar 1st, 2011
 */
#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#include "MPEGFrameHeader.h"
#include "MPEGSideInfo.h"
#include "MPEGFrameData.h"
#include "MPEGGranule.h"

#define DEBUG

MPEGFrameData::MPEGFrameData(FILE *mpeg, MPEGFrameHeader *frameHeader):
        _mpeg(mpeg), _frame_header(frameHeader), _main_data_bitsread(0),
        _end_bits(0), _spectral_bitsread(0), _bytes_read(0), _bits_offset(0){

    _window_no++;

    /* clear internal buffer */
    memset((void *)_data_buffer, 0, MAX_BUFSIZE * sizeof(char));

    /* clear long/short window scalefactor buffer */
    memset((void *)_longwin_scalefac, 0, GRANULE * CHANNEL * LONGWIN_SCALEFACTABLE * sizeof(unsigned int));
    memset((void *)_shortwin_scalefac, 0, GRANULE * CHANNEL * SHORTWIN_PER_GRANULE * SHORTWIN_SCALEFACTABLE * sizeof(unsigned int));

    /* check and extract CRC checksum */
    if(frameHeader->errorProtected()){
        fread(&_crc, sizeof(unsigned short), 1, _mpeg);
    }

    /* extract side information from MPEG file */
    _sideinfo = new MPEGSideInfo(_frame_header, _mpeg);

    /* load data into memory */
    buffer_data();

    int i;
    for(i = 0; i < GRANULE; i++){
        _granules[i] = new MPEGGranule(_mpeg, _frame_header, _sideinfo, this, i);
    }

    /* decode MPEG1-layer III encoding */
    decode(NULL);

    //if((_main_data_bitsread / BITS_PER_BYTE) < (_frame_header->frameSize() - SIDEINFO_SIZE)){
    /* data left in main data buffer */
#ifdef DEBUG
    extern std::ofstream output;

    output << "buffer start before save: " << _bufstart << std::endl;
    output << "buffer end before save: " << _bufend << std::endl;
#endif
        save_to_interbuffer();
#ifdef DEBUG
    output << "buffer start after save: " << _bufstart << std::endl;
    output << "buffer end after save: " << _bufend << std::endl;
#endif
        memset((void *)_data_buffer, 0, MAX_BUFSIZE);
    //}
}

MPEGFrameData::~MPEGFrameData(){
    delete _frame_header;
    _frame_header = NULL;

    /* delete dynamically allocated memory */
    delete _sideinfo;
    int i;
    for(i = 0; i < GRANULE; i++){
        delete _granules[i];
    }
}

/* declare static variables */
//char MPEGFrameData::_data_buffer[MAX_BUFSIZE];
char MPEGFrameData::_interim_buffer[MAX_INTERMEDIATE_BUFSIZE];

int MPEGFrameData::_bufstart;
int MPEGFrameData::_bufend;

int MPEGFrameData::_window_no = 0;

/* extract MDCT encoded MPEG data
 *     if complete successfully, return 0
 *     else -1
 */
int MPEGFrameData::decode(const unsigned char *buf){
    int granule, channel;

    /* skip CRC protection code verification */
    if(!set_bufstart()){
        /* if not enough main_data available, skip frame */
        // save_to_interbuffer();

        return false;
    }

#ifdef DEBUG
    //extern std::ofstream output;
    //output << _bufstart << std::endl;
    //output << _bufend << std::endl;
    //output << std::endl;
    extern std::ofstream huffman_decoded;
    extern std::ofstream restored;
    extern std::ofstream jointstereo;
#endif

    for(granule = 0; granule < GRANULE; granule++){
        for(channel = 0; channel < CHANNEL; channel++){
            /* decode scalefactor for granule and channel */
            //decode_scalefactor(granule, channel);
            decode_scalefactor(granule, channel);
            /* decode huffman encoding */
            decode_huffman(granule, channel);
#ifdef DEBUG
    printHuffman(granule, channel, huffman_decoded);
#endif
            /* restore subband samples */
            _granules[granule]->restoreSamples(channel);
#ifdef DEBUG
    printRestored(granule, channel, restored);
#endif
        }

        /* decode joint stereo for granule */
        _granules[granule]->decodeJointStereo();
#ifdef DEBUG
    printStereo(granule, jointstereo);
#endif
    }

    return OK;
}

/* get MDCT coefficients */
const unsigned char *MPEGFrameData::decodedContent(int granule, int channel) const{
    /* if(granule > GRANULE){
        printf("Error: granule number out of bound.\n");

        return NULL;
    }else{
        _granules[granule]->getRestoredSamples(channel);
    } */
    return NULL;
}

/* get data on which further manipulation is performed */
const ChannelDataDouble *MPEGFrameData::getData(int granule, int channel) const{
    if(granule > GRANULE){
        printf("Error: granule number out of bound.\n");

        return NULL;
    }else{
        return _granules[granule]->getMDCTCoefficients(channel);
    } 
}

/* initialize constant table */
const unsigned int MPEGFrameData::SCALE_LEN[2][16] = {
    {0, 0, 0, 0, 3, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4},   /* scalefactor bit allocation for low frequency */
    {0, 1, 2, 3, 0, 1, 2, 3, 1, 2, 3, 1, 2, 3, 2, 3}    /* scalefactor bit allocation for high frequency */
};

const unsigned int MPEGFrameData::SCALEBAND_TABLE[2][5] = {
    {0, 6, 11, 16, 21},         /* long blocks */
    {0, 6, 12,  0,  0}          /* short blocks */
};

/* buffer main data */
bool MPEGFrameData::buffer_data(){
    _bytes_read = 0;
    _bits_offset = 0;

    int byte_num = _frame_header->frameSize() - SIDEINFO_SIZE;
    if(fread((void *)_data_buffer, sizeof(char), byte_num, _mpeg) != byte_num){
        if(feof(_mpeg)){
#ifdef DEBUG
            printf("EOF of file reached.\n");
#endif
        }else{
            printf("Error reading buffer stream.\n");
        }
    }

    return true;
}

unsigned int MPEGFrameData::readbits(int bit_num){
    unsigned int ret = 0;
    unsigned char mask = 0xFF;              /* used to adjust mask
                                             * padding leading 0 when
                                             * right shifting */

    int bits_read = 0;
    char adjust_char = 0;                   /* hold adjusted character temporarily */
    unsigned char adjust_mask = 0;          /* hold adjusted mask temporarily */

    while(bit_num){
        if(_bufstart != _bufend){           /* data in interim buffer exists */
            int offset = _main_data_bitsread % BITS_PER_BYTE;
            bits_read = MIN(BITS_PER_BYTE - offset, bit_num);
            adjust_char = _interim_buffer[_bufstart] >> (BITS_PER_BYTE - offset - bits_read);
            _bufstart += !((_main_data_bitsread + bits_read) % BITS_PER_BYTE);  /* adjust bufstart pointer */
            if (_bufstart == MAX_INTERMEDIATE_BUFSIZE){
                _bufstart %= MAX_INTERMEDIATE_BUFSIZE;
            }
        }else{
            bits_read = MIN(BITS_PER_BYTE - _bits_offset, bit_num);

            adjust_char = _data_buffer[_bytes_read] >> (BITS_PER_BYTE - _bits_offset - bits_read);

            _bits_offset += bits_read;
            _bytes_read += _bits_offset / BITS_PER_BYTE;
            _bits_offset = _bits_offset % BITS_PER_BYTE;
        }
        adjust_mask = mask >> (BITS_PER_BYTE - bits_read);
        ret <<= bits_read;              /* leave space for newly extracted bits */
        ret |= adjust_char & adjust_mask;

        bit_num -= bits_read;
        _main_data_bitsread += bits_read;
    }

    ret;

    return ret;
}

/* decode scalefactor for specified channel */
void MPEGFrameData::decode_scalefactor(int granule, int channel){
    int window;
    int scalefac_band, scalefac_class;

//#ifdef DEBUG
//    char buffer[256];
//    extern FILE *scale_fact;
//    //extern std::ofstream compress;
//    //extern std::ofstream output;
//    //_sideinfo->printSideInfo(output);
//    //sprintf(buffer, "%d\n", _window_no);
//    //fwrite(buffer, strlen(buffer), 1, scale_fact);
//
//    //extern std::ofstream output;
//    //output << "main data bitsread before scalefactor: " << _main_data_bitsread << std::endl;
//#endif

    /* compare block pattern */
    if(_sideinfo->windowSwitching(granule, channel) &&
       (_sideinfo->windowType(granule, channel) == 2)){
        /* only short window block */
        if(_sideinfo->mixedWindow(granule, channel)){
            /* mix window */
            /* extract 8 long window scale factors corresponding to slen1 */
            for(scalefac_band = 0; scalefac_band < MIXEDLONGWIN_SCALEFACT; scalefac_band++){
                _longwin_scalefac[granule][channel][scalefac_band] =
                    readbits(
                        SCALE_LEN[0][_sideinfo->scalefactorCompress(granule, channel)]
                    );
//#ifdef DEBUG
////      sprintf(buffer, "\n\n%d\n\n", _window_no);
////      fwrite(buffer, 1, strlen(buffer), scale_fact);
//      sprintf(buffer, "long: %d %d %d %d\t", granule, channel, scalefac_band, _longwin_scalefac[granule][channel][scalefac_band]);
//      compress << SCALE_LEN[0][_sideinfo->scalefactorCompress(granule, channel)] << '\t';
//      fwrite(buffer, 1, strlen(buffer), scale_fact);
//#endif
            }
        }else{
            /* short window only */
            /* extract 9 short window scale factors corresponding to slen1 */
            for(scalefac_band = 0; scalefac_band < 3; scalefac_band++){
                for(window = 0; window < SHORTWIN_PER_GRANULE; window++){
                    _shortwin_scalefac[granule][channel][window][scalefac_band] =
                        readbits(
                            SCALE_LEN[0][_sideinfo->scalefactorCompress(granule, channel)]                       
                        );
//#ifdef DEBUG
//      sprintf(buffer, "short: %d %d %d %d %d\t", granule, channel, window, scalefac_band, _shortwin_scalefac[granule][channel][window][scalefac_band]);
//      compress << SCALE_LEN[0][_sideinfo->scalefactorCompress(granule, channel)] << '\t';
//      fwrite(buffer, 1, strlen(buffer), scale_fact);
//#endif
                }
            }
        }

        /* extract 9 short window scale facotrs corresponding to slen1 */
        for(scalefac_band = 3; scalefac_band < 6; scalefac_band++){
            for(window = 0; window < SHORTWIN_PER_GRANULE; window++){
                _shortwin_scalefac[granule][channel][window][scalefac_band] =
                    readbits(
                        SCALE_LEN[0][_sideinfo->scalefactorCompress(granule, channel)]                       
                    );
//#ifdef DEBUG
//      sprintf(buffer, "short: %d %d %d %d %d\t", granule, channel, window, scalefac_band, _shortwin_scalefac[granule][channel][window][scalefac_band]);
//      compress << SCALE_LEN[0][_sideinfo->scalefactorCompress(granule, channel)] << '\t';
//      fwrite(buffer, 1, strlen(buffer), scale_fact);
//#endif
            }
        }

        /* extract 18 scale factors corresponding to slen2 */
        for(scalefac_band = 6; scalefac_band < 12; scalefac_band++){
            for(window = 0; window < 3; window++){
                _shortwin_scalefac[granule][channel][window][scalefac_band] =
                    readbits(
                        SCALE_LEN[1][_sideinfo->scalefactorCompress(granule, channel)]                       
                    );
//#ifdef DEBUG
//      sprintf(buffer, "short: %d %d %d %d %d\t", granule, channel, window, scalefac_band, _shortwin_scalefac[granule][channel][window][scalefac_band]);
//      compress << SCALE_LEN[1][_sideinfo->scalefactorCompress(granule, channel)] << '\t';
//      fwrite(buffer, 1, strlen(buffer), scale_fact);
//#endif
            }
        }

        for(window = 0; window < 3; window++){
            /* fill last scale factor 0 */
            _shortwin_scalefac[granule][channel][window][12] = 0;
//#ifdef DEBUG
//      sprintf(buffer, "short: %d %d %d %d %d\t", granule, channel, window, 12, _shortwin_scalefac[granule][channel][window][12]);
//      fwrite(buffer, 1, strlen(buffer), scale_fact);
//#endif
        }
    }else{
        /* window type 0, 1, 3; LONG */
        for(scalefac_class = 0; scalefac_class < 2; scalefac_class++){
            if((_sideinfo->scalefactorSelection(channel, scalefac_class) == 0) ||
               (granule == 0)){
                   for(scalefac_band = SCALEBAND_TABLE[0][scalefac_class];
                       scalefac_band < SCALEBAND_TABLE[0][scalefac_class + 1];
                       scalefac_band++){
                           _longwin_scalefac[granule][channel][scalefac_band] =
                               readbits(SCALE_LEN[0][_sideinfo->scalefactorCompress(granule, channel)]);
//#ifdef DEBUG
//      sprintf(buffer, "long: %d %d %d %d\t", granule, channel, scalefac_band, _longwin_scalefac[granule][channel][scalefac_band]);
//      compress << SCALE_LEN[0][_sideinfo->scalefactorCompress(granule, channel)] << '\t';
//      fwrite(buffer, 1, strlen(buffer), scale_fact);
//#endif
                   }
            }
        }

        for(scalefac_class = 2; scalefac_class < 4; scalefac_class++){
            if((_sideinfo->scalefactorSelection(channel, scalefac_class) == 0) ||
               (granule == 0)){
                   for(scalefac_band = SCALEBAND_TABLE[0][scalefac_class];
                       scalefac_band < SCALEBAND_TABLE[0][scalefac_class + 1];
                       scalefac_band++){
                           _longwin_scalefac[granule][channel][scalefac_band] =
                               readbits(SCALE_LEN[1][_sideinfo->scalefactorCompress(granule, channel)]);
//#ifdef DEBUG
//      sprintf(buffer, "long: %d %d %d %d\t", granule, channel, scalefac_band, _longwin_scalefac[granule][channel][scalefac_band]);
//      compress << SCALE_LEN[1][_sideinfo->scalefactorCompress(granule, channel)] << '\t';
//      fwrite(buffer, 1, strlen(buffer), scale_fact);
//#endif
                   }
            }
        }
    }

    // uncomment this line if any problem
    _longwin_scalefac[granule][channel][21] = 0;
//#ifdef DEBUG
//      sprintf(buffer, "long: %d %d %d %d\t", granule, channel, 21, _longwin_scalefac[granule][channel][21]);
//      fwrite(buffer, 1, strlen(buffer), scale_fact);
//      fwrite("\n\n", 1, 2, scale_fact);
//      fflush(scale_fact);
//
//      compress << std::endl;
//
//      output << "main data bitsread after scalefactor: " << _main_data_bitsread << std::endl;
//#endif
}

/* decode huffman encoding */
bool MPEGFrameData::decode_huffman(int granule, int channel){
#ifdef DEBUG
    extern std::ofstream output;
    output << _bufstart << std::endl;
    output << _bufend << std::endl;
    output << std::endl;
#endif
    unsigned int x, y, v, w;                /* quantized samples */
    /* find out boundary of each region */
    int region1_start = _sideinfo->region0Samples(granule, channel);
    int region2_start = region1_start + _sideinfo->region1Samples(granule, channel);

#ifdef DEBUG
    output << "spectral_bitsread: " << _spectral_bitsread << std::endl;
#endif

    _end_bits = _spectral_bitsread + _sideinfo->part2_3_length(granule, channel);

#ifdef DEBUG
    output << "end bits: " << _end_bits << std::endl;

    extern std::ofstream huff_table;
    int area;
    for(area = 0; area < 3; area++){
        huff_table << (int)_sideinfo->tableSelect(granule, channel, area) << '\t';
    }

    huff_table << _sideinfo->bigValue(granule, channel) << '\t';
    huff_table << region1_start << '\t';
    huff_table << region2_start << '\t';
    huff_table << std::endl;
#endif

    HuffmanCodeTree *code_tree = NULL;
    /* decode big_values area */
    int i, end_itr = _sideinfo->bigValue(granule, channel) * 2; /* decode pair of big value */
    for(i = 0; i < end_itr; i++){
        if(i < region1_start){              /* decode region0 */
            code_tree = &huff_tree[_sideinfo->tableSelect(granule, channel, 0)];
#ifdef DEBUG
    huff_table << (int)_sideinfo->tableSelect(granule, channel, 0) << std::endl;
#endif
        }else if(i < region2_start){        /* decode region1 */
            code_tree = &huff_tree[_sideinfo->tableSelect(granule, channel, 1)];
#ifdef DEBUG
    huff_table << (int)_sideinfo->tableSelect(granule, channel, 1) << std::endl;
#endif
        }else{                              /* decode region2 */
            code_tree = &huff_tree[_sideinfo->tableSelect(granule, channel, 2)];
#ifdef DEBUG
    huff_table << (int)_sideinfo->tableSelect(granule, channel, 2) << std::endl;
#endif
        }

#ifdef DEBUG
    output << "main data bitread before decode huffman: " << _main_data_bitsread << std::endl;
#endif

        _other_huffman_decoder(code_tree, &x, &y, &v, &w);
        //printf("x=%d, y=%d, v=%d, w=%d\n", x, y, v, w);
        _granules[granule]->setHuffmanSample(x, channel, i / SUBBAND_SAMPLES, i % SUBBAND_SAMPLES);
        i++;
        _granules[granule]->setHuffmanSample(y, channel, i / SUBBAND_SAMPLES, i % SUBBAND_SAMPLES);
    }

#ifdef DEBUG
    output << "main data bitread before decode huffman: " << _main_data_bitsread << std::endl;
#endif

    code_tree = &huff_tree[_sideinfo->count1TableSelect(granule, channel) + 32];

#ifdef DEBUG
    huff_table << "huffman tree index: " << _sideinfo->count1TableSelect(granule, channel) + 32 << std::endl;
#endif

    while((_main_data_bitsread < _end_bits) && (i < GRANULE_SAMPLES)){
        if(_other_huffman_decoder(code_tree, &x, &y, &v, &w)){
            _granules[granule]->setHuffmanSample(v, channel, i / SUBBAND_SAMPLES,
                i % SUBBAND_SAMPLES);
            i++;
            _granules[granule]->setHuffmanSample(w, channel, i / SUBBAND_SAMPLES,
                i % SUBBAND_SAMPLES);
            i++;
            _granules[granule]->setHuffmanSample(x, channel, i / SUBBAND_SAMPLES,
                i % SUBBAND_SAMPLES);
            i++;
            _granules[granule]->setHuffmanSample(y, channel, i / SUBBAND_SAMPLES,
                i % SUBBAND_SAMPLES);
            i++;
        }
    }

    /* dismiss stuffing bits */
#ifdef DEBUG
    output << "main data bitread: " << _main_data_bitsread << std::endl;
    output << "end bits: " << _end_bits << std::endl;
#endif
    if(_main_data_bitsread < _end_bits){
        unsigned int stuffed = _end_bits - _main_data_bitsread;
        int bitsread;
        while(stuffed){
            bitsread = MIN(stuffed, BITS_PER_BYTE);
            readbits(bitsread);
            stuffed -= bitsread;
        }
    }

    _spectral_bitsread = _main_data_bitsread;

    /* sample buffer is intialized zero, no need to zero out the rest */
    return true;
}

/* decode huffman encoding according to given huffman table */
/* for pairs of big_value, 4 lower bits of the decoded value is y */
/* higher bits corresponding to x */
/* for quaduples, only y is meaningful, x is eliminated */
bool MPEGFrameData::huffman_decoder(HuffmanCodeTree *code_tree, unsigned int *x,
    unsigned *y, unsigned int *v, unsigned int *w){
    unsigned int pointer = 0;
    int error = 1;                          /* indicating whether an error occurs */

    if(code_tree->val == NULL){             /* no encoding table exists */
        return true;
    }

    unsigned int level = (unsigned int)1 << (sizeof(unsigned int) * BITS_PER_BYTE - 1);

    /* table 0 needs no bits */
    if(code_tree->treelen == 0){
        *x = *y = 0;

        return true;
    }

    /* look in Huffman table reading data bits */
    /* an array implementing thread tree */
    do{
        if(code_tree->val[pointer][0] == 0){          /* end of tree */
            *x = code_tree->val[pointer][1] >> 4;
            *y = code_tree->val[pointer][1] & 0xf;

            error = 0;
            break;
        }

        if((_main_data_bitsread + 1) > _end_bits){
            return false;
        }

        if(readbits(1)){
            while(code_tree->val[pointer][1] >= MAXOFF){
                pointer += code_tree->val[pointer][1];
            }
            pointer += code_tree->val[pointer][1];
        }else{
            while(code_tree->val[pointer][0] >= MAXOFF){
                pointer += code_tree->val[pointer][0];
            }
            pointer += code_tree->val[pointer][0];
        }

        level >>= 1;
    }while(level || (pointer < huff_tree->treelen));

    if(error){
        printf("Illegal Huffman code.\n");
        /* set x and y a medium value as a simple concealment */
        *x = ((code_tree->xlen - 1) << 1);
        *y = ((code_tree->ylen - 1) << 1);
    }

    /* processing sign encoding for quadruples tables */
    if ((code_tree->tablename[0] == '3') &&
        ((code_tree->tablename[1] == '2') || (code_tree->tablename[1] == '3'))){
        *v = (*y >> 3) & 1;
        *w = (*y >> 2) & 1;
        *x = (*y >> 1) & 1;
        *y = *y & 1;

        if(*v){
            if ((_main_data_bitsread + 1) > _end_bits){
                return false;
            }
            if (readbits(1)){
                *v = -*v;
            }
        }
        if(*w){
            if ((_main_data_bitsread + 1) > _end_bits){
                return false;
            }
            if (readbits(1)){
                *w = -*w;
            }
        }
        if(*x){
            if ((_main_data_bitsread + 1) > _end_bits){
                return false;
            }
            if (readbits(1)){
                *x = -*x;
            }
        }
        if(*y){
            if ((_main_data_bitsread + 1) > _end_bits){
                return false;
            }
            if (readbits(1)){
                *y = -*y;
            }
        }
    }else{
        /* process sign */
        if((code_tree->linbits)){
            if((code_tree->xlen - 1) == *x){
                if((_main_data_bitsread + code_tree->linbits) > _end_bits){
                    *x += readbits(_end_bits - _main_data_bitsread);
                    return false;
                }
                *x += readbits(code_tree->linbits);
            }
            if (*x){
                if((_main_data_bitsread + 1) > _end_bits){
                    return false;
                }
                if(readbits(1)){
                    *x = -*x;
                }
            }

            if((code_tree->ylen - 1) == *y){
                if((_main_data_bitsread + code_tree->linbits) > _end_bits){
                    *x += readbits(_end_bits - _main_data_bitsread);
                    return false;
                }
                *y += readbits(code_tree->linbits);
            }
            if(*y){
                if((_main_data_bitsread + 1) > _end_bits){
                    return false;
                }                
                if(readbits(1)){
                    *y = -*y;
                }
            }
        }
    }

    return true;
}

bool MPEGFrameData::_other_huffman_decoder(HuffmanCodeTree *h, unsigned int *x, unsigned *y,
    unsigned int *v, unsigned int *w){
        unsigned int point = 0;
        int          error = 1;

        if (h->val == NULL)
            return true;

        // 1000...0000
        unsigned int level = (unsigned int)1 << (sizeof(unsigned int)*8-1);

        // Table 0 needs no bits
        if ( h->treelen == 0) {
            *x = *y = 0;
            return true;
        }

        // Lookup in Huffman table reading data bits
        do {
            if (h->val[point][0]==0) {      /* end of tree */
                *x = h->val[point][1] >> 4;
                *y = h->val[point][1] & 0xf;
                error = 0;
                break;
            } 
            if ((_main_data_bitsread + 1) > _end_bits) {
                return false;
            }
            if (readbits(1)) {
                while (h->val[point][1] >= 250) point += h->val[point][1]; 
                point += h->val[point][1];
            } else {
                while (h->val[point][0] >= 250) point += h->val[point][0]; 
                point += h->val[point][0];
            }
            level >>= 1;
        } while (level || (point < huff_tree->treelen) );

        // Check for error
        if (error) { // set x and y to a medium value as a simple concealment
            //cerr << "MaaateP: Illegal Huffman code in data." << endl;
            *x = ((h->xlen-1) << 1);
            *y = ((h->ylen-1) << 1);
        }

        // Process sign encodings for quadruples tables
        if (h->tablename[0] == '3' && (h->tablename[1] == '2' ||
            h->tablename[1] == '3')) {
                *v = (*y>>3) & 1;
                *w = (*y>>2) & 1;
                *x = (*y>>1) & 1;
                *y = *y & 1;

                if (*v) {
                    if ((_main_data_bitsread + 1) > _end_bits) {
                        return false;
                    }
                    if (readbits(1) == 1) *v = -*v;
                }
                if (*w) {
                    if ((_main_data_bitsread + 1) > _end_bits) {
                        return false;
                    }
                    if (readbits(1) == 1) *w = -*w;
                }
                if (*x) {
                    if ((_main_data_bitsread + 1) > _end_bits) {
                        return false;
                    }
                    if (readbits(1) == 1) *x = -*x;
                }
                if (*y) {
                    if ((_main_data_bitsread + 1) > _end_bits) {
                        return false;
                    }
                    if (readbits(1) == 1) *y = -*y;
                }
        } else {
            // Process sign and escape encodings for dual tables

            if (h->linbits)
                if ((h->xlen-1) == *x) {
                    if ((_main_data_bitsread + h->linbits) > _end_bits) {
                        *x += readbits(_end_bits - _main_data_bitsread);
                        return false;
                    }
                    *x += readbits(h->linbits);
                }
                if (*x) {
                    if ((_main_data_bitsread + 1) > _end_bits) {
                        return false;
                    }
                    if (readbits(1) == 1) *x = -*x;
                }
                if (h->linbits)   
                    if ((h->ylen-1) == *y) {
                        if ((_main_data_bitsread + h->linbits) > _end_bits) {
                            *x += readbits(_end_bits - _main_data_bitsread);
                            return false;
                        }
                        *y += readbits(h->linbits);
                    }
                    if (*y) {
                        if ((_main_data_bitsread + 1) > _end_bits) {
                            return false;
                        }
                        if (readbits(1) == 1) *y = -*y;
                    }
        }
        return true;
}

/* sets the starting index of the buffer to the start of main_data
 * skipping any axilliary data from the previous main_data */
bool MPEGFrameData::set_bufstart(){
    unsigned int bytes_buffered = 0;        /* number of bytes in interim buffer */

    if(_bufend != _bufstart){               /* bytes exist in interim buffer */
        if(_bufend < _bufstart){            /* start and end pointer reversed */
            bytes_buffered = (_bufend + MAX_INTERMEDIATE_BUFSIZE) - _bufstart;
        }else{
            bytes_buffered = _bufend - _bufstart;
        }
    }else{
        if(_sideinfo->mainDataBegin() > 0){
            printf("No data available in interim buffer\nCannot rewind %d.\n", _sideinfo->mainDataBegin());

            return false;
        }else{
            return true;
        }
    }

    if(_sideinfo->mainDataBegin() > bytes_buffered){
        printf("No enough main data available in interim buffer\nCannot rewind %d.\n",
               _sideinfo->mainDataBegin() - bytes_buffered);

        return false;
    }

    /* enough data buffered */
    _bufstart += bytes_buffered - _sideinfo->mainDataBegin();
    _bufstart %= MAX_INTERMEDIATE_BUFSIZE;
    _bits_offset = 0;

    return true;
}

/* saves the remaining data of the frame to the interim buffer */
bool MPEGFrameData::save_to_interbuffer(){
    // in case intermediate buffer overflowed
    int overflow = 0;
    int copy_count = _frame_header->frameSize() - SIDEINFO_SIZE - _bytes_read;
#ifdef DEBUG
    extern std::ofstream output;
    output << "in save to interim, bytes read: " << _bytes_read << std::endl;
    output << "frame size: " << _frame_header->frameSize() << std::endl;
    output << "left: " << copy_count << std::endl;
#endif

    while(copy_count--){
        _interim_buffer[_bufend++] = _data_buffer[_bytes_read++];
        _bufend %= MAX_INTERMEDIATE_BUFSIZE;

        if(_bufend == _bufstart){
            overflow++;
            _bufstart++;
            _bufstart %= MAX_INTERMEDIATE_BUFSIZE;
        }
    }

    if(overflow){
        printf("WARNING: buffer overflow in interim buffer %d\n", overflow);
    }

    return true;
}

/* define and allocate storage space */
HuffmanCodeTree huff_tree[HUFFMANTABLE_COUNTS];

bool buildHuffmanTree(){
    const unsigned char *p;
    int t = 0;

    int i, j;
    for(i = 0; i < HUFFMANTABLE_COUNTS; i++){
        sprintf(huff_tree[i].tablename, "%d", i);
        huff_tree[i].treelen = huff_tab[i].treelen;
        huff_tree[i].xlen = huff_tab[i].xlen;
        huff_tree[i].ylen = huff_tab[i].ylen;
        huff_tree[i].linbits = huff_tab[i].linbits;
        huff_tree[i].linmax = (1 << huff_tab[i].linbits) - 1;

#ifdef DEBUG
        int tab_no;
        sscanf(huff_tree[i].tablename, "%u", &tab_no);
        if(tab_no !=i ){
            printf("Wrong table number.\n");

            return false;
        }
#endif

        t = huff_tab[i].ref;
        if(t >= 0){                     /* assign referred huffman tree property */
            huff_tree[i].ref = t;
            huff_tree[i].val = huff_tree[t].val;
            huff_tree[i].treelen = huff_tree[t].treelen;

            if(!((huff_tree[i].xlen == huff_tree[t].xlen) &&
                 (huff_tree[i].ylen == huff_tree[t].ylen))){
                printf("Wrong table reference %d\n", i);
            }
        }else if(t == -1){              /* load huffman tree from table */
            huff_tree[i].ref = -1;
            huff_tree[i].val = (unsigned char(*)[2])calloc(2 * (huff_tree[i].treelen) + 1,
                                sizeof(unsigned char));
            if(!huff_tree[i].val){
                printf("Heap error while loading table %d\n", i);

                return false;
            }

            p = huff_tab[i].data;
            for(j = 0; j < huff_tree[i].treelen; j++){
                huff_tree[i].val[j][0] = *p++;
                huff_tree[i].val[j][1] = *p++;
            }
        }else{
            printf("Huffman decode table error at %d\n", i);

            return false;
        }
    }

    return (i == HUFFMANTABLE_COUNTS);
}

#ifdef DEBUG
// parse the scale factors (MPEG1)
 void 
MPEGFrameData::decode_scale_I(int gr, int ch)
{

    //this->printSideinfo(output);

  int window;
  unsigned int sfb, scfc;
  extern FILE *scale_fact;
  extern std::ofstream compress;
  char buffer[256];
//sprintf(buffer, "\n\n%d\n\n", frame->windowNo);
//fwrite(buffer, 1, strlen(buffer), scale_fact);
    
  if (_sideinfo->windowSwitching(gr,ch) && (_sideinfo->windowType(gr,ch) == 2)) { 
	
      if (_sideinfo->mixedWindow(gr,ch)) {	/// MIXED
      for (sfb=0; sfb<8; sfb++) {
                _longwin_scalefac[gr][ch][sfb] = readbits(slen1(ch,gr));
sprintf(buffer, "%d\t", _longwin_scalefac[gr][ch][sfb]);
compress << slen1(ch,gr) << '\t';
fwrite(buffer, 1, strlen(buffer), scale_fact);
      }
      for (sfb=3; sfb<6; sfb++) {
	for (window=0; window<3; window++) {
	  _shortwin_scalefac[gr][ch][window][sfb] 
	    = readbits(slen1(ch,gr));
sprintf(buffer, "%d\t", _shortwin_scalefac[gr][ch][window][sfb]);
compress << slen1(ch,gr) << '\t';
fwrite(buffer, 1, strlen(buffer), scale_fact);
	}
      }
      for (sfb = 6; sfb < 12; sfb++) {
	for (window=0; window<3; window++) {
	  _shortwin_scalefac[gr][ch][window][sfb] 
	    = readbits(slen2(ch,gr));
sprintf(buffer, "%d\t", _shortwin_scalefac[gr][ch][window][sfb]);
compress << slen2(ch,gr) << '\t';
fwrite(buffer, 1, strlen(buffer), scale_fact);
	}
      }
      for (window=0; window<3; window++) {
	_shortwin_scalefac[gr][ch][window][12] = 0;
sprintf(buffer, "%d\t", _shortwin_scalefac[gr][ch][window][12]);
fwrite(buffer, 1, strlen(buffer), scale_fact);
      }
	    
    } else { // not mixed; SHORT
	    
      for (sfb=0; sfb<6; sfb++) {
	for (window=0; window<3; window++) {
	  _shortwin_scalefac[gr][ch][window][sfb] 
	    = readbits (slen1(ch,gr));
sprintf(buffer, "%d\t", _shortwin_scalefac[gr][ch][window][sfb]);
compress << slen1(ch,gr) << '\t';
fwrite(buffer, 1, strlen(buffer), scale_fact);
	}
      }
      for (sfb=6; sfb<12; sfb++) {
	for (window=0; window<3; window++) {
	  _shortwin_scalefac[gr][ch][window][sfb] 
	    = readbits (slen2(ch,gr));
sprintf(buffer, "%d\t", _shortwin_scalefac[gr][ch][window][sfb]);
compress << slen2(ch,gr) << '\t';
fwrite(buffer, 1, strlen(buffer), scale_fact);
	}
      }
      for (window=0; window<3; window++) {
	_shortwin_scalefac[gr][ch][window][12] = 0;
sprintf(buffer, "%d\t", _shortwin_scalefac[gr][ch][window][12]);
fwrite(buffer, 1, strlen(buffer), scale_fact);
      }
    }
	
  } else {   // blocktypes 0,1,3; LONG
	
    for (scfc=0; scfc<2; scfc++) {
        if ((_sideinfo->scalefactorSelection(ch,scfc) == 0) || (gr == 0)) {
	for (sfb=SCALEBAND_TABLE[0][scfc]; 
	     sfb<SCALEBAND_TABLE[0][scfc+1];
	     sfb++) {
	  _longwin_scalefac[gr][ch][sfb] = readbits(slen1(ch,gr));
sprintf(buffer, "%d\t", _longwin_scalefac[gr][ch][sfb]);
compress << slen1(ch,gr) << '\t';
fwrite(buffer, 1, strlen(buffer), scale_fact);
	}
      }
    }
    for (scfc=2; scfc<4; scfc++) {
      if ((_sideinfo->scalefactorSelection(ch,scfc) == 0) || (gr == 0)) {
	for (sfb=SCALEBAND_TABLE[0][scfc]; 
	     sfb<SCALEBAND_TABLE[0][scfc+1];
	     sfb++) {
	  _longwin_scalefac[gr][ch][sfb] = readbits(slen2(ch,gr));
sprintf(buffer, "%d\t", _longwin_scalefac[gr][ch][sfb]);
compress << slen2(ch,gr) << '\t';
fwrite(buffer, 1, strlen(buffer), scale_fact);
	}
      }
    }
	
  }
  _longwin_scalefac[gr][ch][21] = 0;
    sprintf(buffer, "%d\t", _longwin_scalefac[gr][ch][21]);
    fwrite(buffer, 1, strlen(buffer), scale_fact);
    fwrite("\n\n", 1, 2, scale_fact);
    fflush(scale_fact);

    compress << std::endl;

  return;
}

 unsigned int 
MPEGFrameData::slen1 (unsigned int channel, unsigned int granule)
{
  return SCALE_LEN[0][_sideinfo->scalefactorCompress(granule, channel)];
}

// returns bitallocation for scalefactors for higher frequency part
 unsigned int 
MPEGFrameData::slen2 (unsigned int channel, unsigned int granule)
{
  return SCALE_LEN[1][_sideinfo->scalefactorCompress(granule, channel)];
}


#ifdef DEBUG
    void MPEGFrameData::printHuffman(int granule, int channel, std::ofstream &output){
        long *data = (long *)_granules[granule]->getDeHuffmanSamples(channel);
        output << "granule " << granule << "channel " << channel << std::endl;
        int index;
        for (index = 0; index < 576; index++){
            output << data[index] << '\t';
        }

        output << std::endl;
    }

    void MPEGFrameData::printRestored(int granule, int channel, std::ofstream &output){
        double *data = (double *)_granules[granule]->getRestoredSamples(channel);
        output << "granule " << granule << "channel " << channel << std::endl;
        int index;
        for (index = 0; index < 576; index++){
            output << data[index] << '\t';
        }

        output << std::endl;
    }

    void MPEGFrameData::printStereo(int granule, std::ofstream &output){
        int channel;
        for(channel = 0; channel < 2; channel++){
            double *data = (double *)_granules[granule]->getMDCTCoefficients(channel);
            output << "granule " << granule << "channel " << channel << std::endl;
            int index;
            for (index = 0; index < 576; index++){
                output << data[index] << '\t';
            }
            output << std::endl;
        }
    }
#endif

#endif